package com.example.supplychainbackend.service.Impl;

import com.example.supplychainbackend.controller.dto.blockchain.UploadInfo;
import com.example.supplychainbackend.controller.dto.design.OrderDto;
import com.example.supplychainbackend.controller.dto.sales.*;
import com.example.supplychainbackend.dao.sales.*;
import com.example.supplychainbackend.entity.sales.*;
import com.example.supplychainbackend.mapper.SalesMapper;
import com.example.supplychainbackend.service.BlockchainService;
import com.example.supplychainbackend.service.SalesService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class SalesServiceImpl implements SalesService {
    private final CustomerDao customerDao;
    private final CustomerPaymentDao paymentDao;
    private final CustomerInvoiceDao invoiceDao;

    private final ContractDao contractDao;

    private final OrderDao orderDao;

    private final SalesMapper mapper;
    private final BlockchainService blockchainService;

    @Override
    public String createCustomer(CustomerDetailDto customerDetailDto) {
        CustomerEntity entity = mapper.toCustomerEntity(customerDetailDto);
        entity = customerDao.save(entity);
        List<PaymentInfoDto> payments = customerDetailDto.getPayment();
        if (payments!=null){
            for (PaymentInfoDto dto:
                    payments) {
                CustomerPaymentEntity paymentE = mapper.toPaymentEntity(dto);
                paymentE.setId(null);
                paymentE.setCustomerId(String.valueOf(entity.getId()));
                paymentDao.save(paymentE);
            }
        }
        List<InvoiceInfoDto> invoices = customerDetailDto.getInvoice();
        if (invoices!=null){
            for (InvoiceInfoDto dto:
                    customerDetailDto.getInvoice()) {
                CustomerInvoiceEntity invoiceE = mapper.toInvoiceEntity(dto);
                invoiceE.setId(null);
                invoiceE.setCustomerId(String.valueOf(entity.getId()));
                invoiceDao.save(invoiceE);
            }
        }
        return entity.getId();
    }

    @Transactional
    public CustomerDetailDto updateCustomer(String id, CustomerDetailDto customerDetailDto) {
        paymentDao.deleteByCustomerId(String.valueOf(id));
        invoiceDao.deleteByCustomerId(String.valueOf(id));
        this.createCustomer(customerDetailDto);
        return this.getCustomer(id);
    }

    @Override
    public CustomerDetailDto getCustomer(String id) {
        Optional<CustomerEntity> res = customerDao.findById(id);
        if (res.isPresent()){
            CustomerDetailDto dto = mapper.toDetailDto(res.get());
            List<CustomerPaymentEntity> payments = paymentDao.findAllByCustomerId(String.valueOf(id));
            List<PaymentInfoDto> paymentInfoDtos = new ArrayList<>();
            for (CustomerPaymentEntity p:
                 payments) {
                paymentInfoDtos.add(mapper.toPaymentDto(p));
            }
            dto.setPayment(paymentInfoDtos);

            List<CustomerInvoiceEntity> invoices = invoiceDao.findAllByCustomerId(String.valueOf(id));
            List<InvoiceInfoDto> invoiceInfoDtos = new ArrayList<>();
            for (CustomerInvoiceEntity i:
                 invoices) {
                invoiceInfoDtos.add(mapper.toInvoiceDto(i));
            }
            dto.setInvoice(invoiceInfoDtos);
            return dto;
        }

        return null;
    }

    @Override
    public List<CustomerDto> getAllCustomer(Long eid) {
        List<CustomerEntity> customers = customerDao.findAll();
        List<CustomerDto> dtos = new ArrayList<>();
        for (CustomerEntity c:
             customers) {
            dtos.add(mapper.toCustomerDto(c));
        }
        return dtos;
    }

    @Transactional
    public Long deleteCustomer(String id) {
        customerDao.deleteById(id);
        paymentDao.deleteByCustomerId(String.valueOf(id));
        invoiceDao.deleteByCustomerId(String.valueOf(id));
        return 1L;
    }

    @Override
    public String createContract(ContractDto contractDto) {
        ContractEntity entity = mapper.toContractEntity(contractDto);
        entity.setContractStatus("PENDING_COMPLETION");
        entity.setApprovalStatus("APPROVED");
        entity.setExecutionStatus("NOT_EXECUTED");

        entity.setOnChain(false);

        List<DeliverableEntity> dlbs = entity.getDeliverable();
        if (dlbs != null) {
            for (DeliverableEntity dlb:
                    dlbs) {
                dlb.setId(null);
            }
            entity.setDeliverable(dlbs);
        }


        return contractDao.save(entity).getContractNumber();

    }

    @Override
    public ContractDto updateContract(String contractNumber, ContractDto contractDto) {
        ContractEntity originEntity = contractDao.findByContractNumber(contractNumber);
        if (originEntity == null) {
            throw new RuntimeException("contract id "+ contractNumber+ " not exists");
        }
        if (originEntity.getOnChain())
            throw new RuntimeException("on-chain data");
        ContractEntity entity = mapper.toContractEntity(contractDto);
        entity.setOnChain(false);
        entity.setContractNumber(contractNumber);
        entity = contractDao.save(entity);
        return mapper.toContractDto(entity);
    }

    @Override
    public ContractDto getContract(String contractNumber) {
        ContractEntity entity = contractDao.findByContractNumber(contractNumber);
        if (entity == null){
            throw new RuntimeException("contract id "+ contractNumber +" not found");
        }
        return mapper.toContractDto(entity);
    }

    @Override
    public List<ContractDto> getAllContract(Long eid) {
        List<ContractEntity> entities = contractDao.findAll();
        if (eid!=0L)
            entities = contractDao.findAllByEnterpriseId(eid.toString());
        return mapper.toContractDtoList(entities);
    }

    @Override
    @Transactional
    public Long deleteContract(String contractNumber) {
        return contractDao.deleteByContractNumber(contractNumber);
    }

    @Override
    public UploadInfo uploadToChainContract(String contractNumber) {
        ContractDto contract = this.getContract(contractNumber);
        if (null!=contract){
            UploadInfo txInfo = blockchainService.uploadSalesContract(contractNumber, contract.toString());
            contract.setOnChain(true);
            contract.setCommitTime(txInfo.getCommitTime());
            contract.setTransactionId(txInfo.getTransactionId());
            ContractEntity entity = mapper.toContractEntity(contract);
            entity = contractDao.save(entity);
            return txInfo;
        }
        throw new RuntimeException("contract not found");
    }

    @Override
    @Transactional
    public String createOrder(OrderDetailDto form) {
        // use this to check if number valid
        this.getContract(form.getContractNumber());

        OrderEntity orderEntity = mapper.toOrderEntity(form);
        orderEntity.setOnChain(false);
        return orderDao.save(orderEntity).getId();
    }

    @Override
    @Transactional
    public OrderDetailDto updateOrder(String id, OrderDetailDto form) {
        OrderEntity originEntity = orderDao.findById(id).get();
        if (originEntity.getOnChain())
            throw new RuntimeException("on-chain data");
        OrderEntity orderEntity = mapper.toOrderEntity(form);
        return mapper.toOrderDetailDto(orderDao.save(orderEntity));
    }

    @Override
    public OrderDetailDto getOrder(String id) {
        OrderEntity orderEntity = orderDao.findById(id).get();
        return mapper.toOrderDetailDto(orderEntity);
    }

    @Override
    @Transactional
    public String deleteOrder(String id) {
        orderDao.deleteById(id);
        return id;
    }

    @Override
    public UploadInfo uploadToChainOrder(String orderNumber) {
        OrderDetailDto order = this.getOrder(orderNumber);
        if (null!=order){
            UploadInfo txInfo = blockchainService.uploadSalesOrder(orderNumber, order.toString());
            order.setOnChain(true);
            order.setCommitTime(txInfo.getCommitTime());
            order.setTransactionId(txInfo.getTransactionId());
            orderDao.save(mapper.toOrderEntity(order));
            return txInfo;
        }
        throw new RuntimeException("order not found");
    }

    @Override
    public List<OrderDetailDto> getAllOrder() {
        List<OrderEntity> res = orderDao.findAll();
        return mapper.toOrderDetailDtoList(res);
    }
    private List<OrderDetailDto> getAllOrderByEid(String eid) {
        List<OrderEntity> res = orderDao.findAllByEnterpriseId(eid);
        return mapper.toOrderDetailDtoList(res);
    }

    @Override
    public List<OrderListItemDto> listOrderItems() {
        List<OrderEntity> entities = orderDao.findAll();
        return mapOrderItems(entities);
    }
    @Override
    public List<OrderListItemDto> listOrderItemsByEid(String eid) {
        List<OrderEntity> entities = orderDao.findAllByEnterpriseId(eid);
        return mapOrderItems(entities);
    }

    private List<OrderListItemDto> mapOrderItems(List<OrderEntity> entities){
        List<OrderListItemDto> resList = new ArrayList<>();
        for (OrderEntity oe:
                entities) {
            ContractEntity contract = contractDao.findByContractNumber(oe.getContractNumber());
            SalesInfoEntity salesInfo = oe.getSalesInfo();
            OrderListItemDto.OrderListItemDtoBuilder tmpItemBuilder = OrderListItemDto.builder()
                    .id(oe.getId())
                    .salesOrderNumber(oe.getId())
                    .salesOrderType(oe.getSalesOrderType()).salesOrderName(oe.getSalesOrderName())
                    .customerName(contract.getCustomerName()).importance(salesInfo.getImportance())
                    .requiredDeliveryDate(oe.getRequiredDeliveryDate()).orderLineCategory("NORMAL")
                    .contractNumber(oe.getContractNumber()).contractName(contract.getContractName())
                    .destination(salesInfo.getDestination()).localCurrency(contract.getLocalCurrency())
                    .originalCurrency(contract.getOriginalCurrency()).exchangeRate((float) salesInfo.getExchangeRate())
                    .salesman(salesInfo.getSalesman()).salesChannel(oe.getSalesChannel()).email(salesInfo.getEmail())
                    .orderDate(oe.getOrderDate()).tradeTerms(salesInfo.getTradeTerms()).remarks(salesInfo.getRemarks())
                    .businessDepartment(contract.getBusinessDepartment()).orderType(1)
                    .firstSendTime(oe.getFirstSendTime()).latestResendTime(oe.getLatestResendTime())
                    .company(oe.getCompany()).salesOrganization(oe.getSalesOrganization())
                    .deliveryCustomerName(salesInfo.getShipToParty()).soldToCustomerName(salesInfo.getSoldToParty())
                    .approver("自动通过")
                    .onChain(oe.getOnChain()).transactionId(oe.getTransactionId()).commitTime(oe.getCommitTime())
                    .userName(oe.getUserName()).userId(oe.getUserId())
                    .enterpriseName(oe.getEnterpriseName()).enterpriseId(oe.getEnterpriseId());
            List<DeliverableEntity> deliverables = contract.getDeliverable();
            if (deliverables != null && !deliverables.isEmpty()) {
                DeliverableEntity deliverable = deliverables.get(0);
                tmpItemBuilder.materialNumber(deliverable.getMaterialNumber())
                        .materialName(deliverable.getMaterialName())
                        .customerMaterialName(deliverable.getCustomerMaterialName())
                        .customerMaterialNumber(deliverable.getCustomerMaterialNumber())
                        .cabinetNumber(deliverable.getCabinetNumber())
                        .materialDescription(deliverable.getMaterialDescription())
                        .quantity(Float.valueOf(deliverable.getQuantity()))
                        .originalTotalAmount(Float.valueOf(deliverable.getAmountBeforeTax()))
                        .originalTotalAmountWithTax(Float.valueOf(deliverable.getTotalAmountWithTax()));
            }
            OrderListItemDto tmpItem = tmpItemBuilder.build();
            if(null == tmpItem.getOriginalTotalAmount() || null == tmpItem.getOriginalTotalAmountWithTax()){
                tmpItem.setOriginalTotalAmount(Float.valueOf(contract.getContractAmount()));
                tmpItem.setOriginalTotalAmountWithTax(Float.valueOf(contract.getContractAmount()));
            }
            resList.add(tmpItem);
        }
        return resList;
    }

    @Override
    public OrderDto getDesignOrder(String id) {
        return mapOrder(id);
    }

    @Override
    public List<OrderDto> getAllDesignOrders() {
        List<OrderDetailDto> orderDetailDtos = getAllOrder();
        List<OrderDto> res = new ArrayList<>();
        for (OrderDetailDto od:
                orderDetailDtos) {
            res.add(mapOrder(od.getId()));
        }

        return res;
    }
    @Override
    public List<OrderDto> getAllDesignOrdersByEid(String eid) {
        List<OrderDetailDto> orderDetailDtos = getAllOrderByEid(eid);
        List<OrderDto> res = new ArrayList<>();
        for (OrderDetailDto od:
                orderDetailDtos) {
            res.add(mapOrder(od.getId()));
        }

        return res;
    }

    public OrderDto mapOrder(String id) {
        OrderDetailDto dto = getOrder(id);
        ContractDto cDto = getContract(dto.getContractNumber());
        SalesInfoDto salesInfoDto = dto.getSalesInfo();
        OrderDto res = new OrderDto();

        res.setSalesOrderNumber(String.valueOf(dto.getId()));
        res.setSalesOrderName(dto.getSalesOrderName());
        res.setOrderType(dto.getOrderType());

        res.setDeliveryDate(dto.getRequiredDeliveryDate());
        res.setContractNumber(dto.getContractNumber());

        res.setContractName(cDto.getContractName());
        res.setLocalCurrency(cDto.getLocalCurrency());
        res.setOriginalCurrency(cDto.getOriginalCurrency());
        res.setDepartment(cDto.getBusinessDepartment());
        res.setDeliverable(cDto.getDeliverable());

        res.setSalesman(salesInfoDto.getSalesman());
        res.setCreateTime(dto.getOrderDate());
        res.setStatus(3);
        res.setExchangeRate(salesInfoDto.getExchangeRate());
        res.setImportance(salesInfoDto.getImportance());
        res.setEmail(salesInfoDto.getEmail());
        res.setTradeTerms(salesInfoDto.getTradeTerms());
        res.setDestination(salesInfoDto.getDestination());
        res.setRemark(salesInfoDto.getRemarks());
        res.setCreateBy(cDto.getSignatory());


        res.setUserName(dto.getUserName());
        res.setEnterpriseName(dto.getEnterpriseName());
        res.setOnChain(dto.getOnChain());
        res.setTransactionId(dto.getTransactionId());
        res.setCommitTime(dto.getCommitTime());
        res.setUserId(dto.getUserId());
        res.setEnterpriseId(dto.getEnterpriseId());
        return res;
    }
}
